//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// File types handling.
//

# include "StdAfx.h"
# include "Resource.h"
# include "FileTypeInfo.h"
# include "Load.h"
# include "Save.h"
# include "Map.h"
# include "MapView.h"
# include "Status.h"
# include "Menu.h"
# include "Settings.h"
# include "Wrappers.h"
# include "Globals.h"

CFileTypeInfo * CFileTypeInfo::s_pFirst;
size_t CFileTypeInfo::s_cMaxIndex = 0;

CFileTypeInfo::CFileTypeInfo (
	pfCreateLoader_t _pfCreateLoader, pfCreateSaver_t _pfCreateSaver,
	const char * _strClass,	const char * _strExt, const char * _strFilter,
	DWORD _dwFlags
) :
	m_pfCreateLoader (_pfCreateLoader),
	m_pfCreateSaver (_pfCreateSaver),
	m_strClass (_strClass),
	m_dwFlags (_dwFlags),
	m_cOrdinal (CFileTypeInfo::s_cMaxIndex ++),
	m_bEnableSave (true)
{
	char strId [32];
	::sprintf (strId, "EnableSave_%s", m_strClass);
	m_bEnableSave = GetSetting (strId, true);

	// Prepare extensions.
	const char * pBeginExt = _strExt;
	for (const char * p = _strExt; * p; ++ p) {
		if (* p == ';') {
			m_vecExts.push_back (string_t (pBeginExt, p));
			++ p;
			pBeginExt = p;
		}
	}
	if (pBeginExt < p)
		m_vecExts.push_back (string_t (pBeginExt, p));

	// Prepare filter.
	m_strFilter.assign (_strFilter);
	m_strFilter.append (1, '\0');
	for (size_t c = 0; c < m_vecExts.size (); ++ c) {
		m_strFilter.append ("*.", 2);
		m_strFilter.append (m_vecExts [c]);
		if (c != m_vecExts.size () - 1)
			m_strFilter.append (1, ';');
	}

	// Insert to the head of the list.
	m_pNext = CFileTypeInfo::s_pFirst;
	CFileTypeInfo::s_pFirst = this;
}

void CFileTypeInfo::EnableSave (bool _bEnable) {
	m_bEnableSave = _bEnable;

	char strId [32];
	::sprintf (strId, "EnableSave_%s", m_strClass);
	SaveSetting (strId, _bEnable);
}

static
int __cdecl _CompareFTI (const void * _p1, const void * _p2) {
	const CFileTypeInfo * const pFTI1 = * reinterpret_cast<const CFileTypeInfo * const *> (_p1);
	const CFileTypeInfo * const pFTI2 = * reinterpret_cast<const CFileTypeInfo * const *> (_p2);

	if (::strcmp (pFTI1->m_strClass, "polish") == 0)
		return -1;
	if (::strcmp (pFTI2->m_strClass, "polish") == 0)
		return 1;

	return ::strcmp (pFTI1->m_strClass, pFTI2->m_strClass);
}

void CFileTypeInfo::Sort () {
	CFileTypeInfo * p;

	size_t cFileTypeInfos = 0;
	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext)
		++ cFileTypeInfos;
	if (cFileTypeInfos == 0)
		return;

	CFileTypeInfo ** const ppFileTypeInfos = (CFileTypeInfo **) alloca (sizeof (CFileTypeInfo *)*cFileTypeInfos);

	size_t c = 0;
	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext)
		ppFileTypeInfos [c ++] = p;

	::qsort (ppFileTypeInfos, cFileTypeInfos, sizeof (CFileTypeInfo *), _CompareFTI);

	CFileTypeInfo::s_pFirst = ppFileTypeInfos [0];
	for (c = 0; c < cFileTypeInfos - 1; ++ c)
		ppFileTypeInfos [c]->m_pNext = ppFileTypeInfos [c + 1];
	ppFileTypeInfos [cFileTypeInfos - 1]->m_pNext = NULL;
}

CFileTypeInfo * CFileTypeInfo::FindSaverInfo () const {
	return FindSaverInfoByClass (m_strClass);
}

CFileTypeInfo * CFileTypeInfo::FindSaverInfoByClass (const char * _strClass) {
	for (CFileTypeInfo * p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext)
		if (p->m_pfCreateSaver && ::stricmp (_strClass, p->m_strClass) == 0)
			return p;
	return NULL;
}

const CFileTypeInfo * CFileTypeInfo::GetDefaultSaverInfo () {
	for (const CFileTypeInfo * p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext)
		if (p->m_pfCreateSaver && (p->m_dwFlags & ftiDefault))
			return p;
	return NULL;
}

CMapLoader * CFileTypeInfo::CreateLoader (
	const char * _pData, size_t _cFileSize, const char * _strFileName,
	const CFileTypeInfo ** _ppSaverInfo, DWORD & _dwFlags
) {
	assert (CFileTypeInfo::s_pFirst);
	if (CFileTypeInfo::s_pFirst == NULL)
		return NULL;

	// Get file name extension.
	const char * strExt = ::strrchr (_strFileName, '.');
	if (strExt != NULL) {
		++ strExt;

		// Search for the creator associated for the extension.
		for (const CFileTypeInfo * p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
			if (p->m_pfCreateLoader) {
				bool bFits = false;
				for (size_t c = 0; c < p->m_vecExts.size (); ++ c)
					if (::stricmp (strExt, p->m_vecExts [c].c_str ()) == 0) {
						bFits = true;
						break;
					}
				if (bFits) {
					CMapLoader * const pMapLoader = p->m_pfCreateLoader ();
					if (pMapLoader->IsValid (_pData, _cFileSize)) {
						CFileTypeInfo * const pSaverInfo = p->FindSaverInfo ();
						if (pSaverInfo)
							pSaverInfo->EnableSave (true);

						* _ppSaverInfo = pSaverInfo;
						_dwFlags       = p->m_dwFlags;
						return pMapLoader;
					}
					delete pMapLoader;
				}
			}
		}
	}

	// No association found.
	* _ppSaverInfo = NULL;
	_dwFlags = 0;
	return NULL;
}

void CFileTypeInfo::ShowOpenFile (bool _bRemoveOldMap) {
	ShowOpenFile (_bRemoveOldMap, false, g_map, 0, NULL);
}

void CFileTypeInfo::GetOpenFileFilter (string_t & _strFilter, DWORD _dwRequiredFlagsFilter) {
	//
	// Prepare ofn.lpstrFilter.
	//
	const CFileTypeInfo * p;

	static const char strAllFiles [] = "All files (*.*)\0*.*\0";

	// Prepare sub-filter for all supported types.
	string_t strAllSupportedFiles = "All supported files (";
	typedef std::set<string_t> exts_t;
	exts_t exts;
	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if (_dwRequiredFlagsFilter != 0 && (_dwRequiredFlagsFilter & p->m_dwFlags) == 0)
			continue;

		if ((p->m_dwFlags & ftiImportExport) == 0 && p->m_pfCreateLoader) {
			for (size_t c = 0; c < p->m_vecExts.size (); ++ c)
				exts.insert (p->m_vecExts [c]);
		}
	}

	exts_t::const_iterator e;
	for (e = exts.begin (); e != exts.end (); ++ e) {
		if (e != exts.begin ())
			strAllSupportedFiles.append (",");
		strAllSupportedFiles.append ("*.");
		strAllSupportedFiles.append (* e);
	}
	strAllSupportedFiles.append (")\0", 2);
	for (e = exts.begin (); e != exts.end (); ++ e) {
		if (e != exts.begin ())
			strAllSupportedFiles.append (";");
		strAllSupportedFiles.append ("*.");
		strAllSupportedFiles.append (* e);
	}
	strAllSupportedFiles.append ("\0", 1);

	// Estimate the size of the filter.
	size_t cFilterLen = sizeof (strAllFiles) + strAllSupportedFiles.size ();
	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if (_dwRequiredFlagsFilter != 0 && (_dwRequiredFlagsFilter & p->m_dwFlags) == 0)
			continue;

		if ((p->m_dwFlags & ftiImportExport) == 0 && p->m_pfCreateLoader)
			cFilterLen += p->m_strFilter.size () + 1;
	}

	// Build the filter.
	char * const strFilter = reinterpret_cast<char *> (::alloca (cFilterLen));

	::memcpy (strFilter, strAllSupportedFiles.c_str (), strAllSupportedFiles.size ());
	size_t cOffset = strAllSupportedFiles.size ();

	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if (_dwRequiredFlagsFilter != 0 && (_dwRequiredFlagsFilter & p->m_dwFlags) == 0)
			continue;

		if ((p->m_dwFlags & ftiDefault) && p->m_pfCreateLoader) {
			// Put the default filter second.
			::memcpy (strFilter + cOffset, p->m_strFilter.c_str (), p->m_strFilter.size () + 1);
			cOffset += p->m_strFilter.size () + 1;
			break;
		}
	}

	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if (_dwRequiredFlagsFilter != 0 && (_dwRequiredFlagsFilter & p->m_dwFlags) == 0)
			continue;

		if ((p->m_dwFlags & ftiDefault) || (p->m_dwFlags & ftiImportExport) || ! p->m_pfCreateLoader)
			continue;
		::memcpy (strFilter + cOffset, p->m_strFilter.c_str (), p->m_strFilter.size () + 1);
		cOffset += p->m_strFilter.size () + 1;
	}
	::memcpy (strFilter + cOffset, strAllFiles, sizeof (strAllFiles));

	_strFilter.assign (strFilter, cFilterLen - 1);
}

void CFileTypeInfo::ShowOpenFile (bool _bRemoveOldMap, bool _bWait, map_t & _map, DWORD _dwRequiredFlagsFilter, const char * _strDefaultName) {
	char strFilesName [(MAX_PATH + 1)*100 + 1];
	if (_strDefaultName)
		::strncpy (strFilesName, _strDefaultName, MAX_PATH);
	else
		strFilesName [0] = '\0';

	OPENFILENAME ofn;
	::ZeroMemory (& ofn, sizeof (OPENFILENAME));
	ofn.lStructSize = sizeof (OPENFILENAME);
	ofn.hwndOwner   = g_hMapWnd;
	ofn.hInstance   = g_hResInst;
	ofn.lpstrTitle  = _bRemoveOldMap ? NULL : "Add";
	ofn.lpstrFile   = strFilesName;
	ofn.nMaxFile    = sizeof (strFilesName);
	ofn.Flags       = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT;

	//
	// Prepare ofn.lpstrFilter.
	//
	string_t strFilter;
	GetOpenFileFilter (strFilter, _dwRequiredFlagsFilter);
	ofn.lpstrFilter = strFilter.c_str ();

	//
	// Prepare ofn.nFilterIndex.
	//
	static size_t s_cFilterIndex = 0;
	ofn.nFilterIndex = s_cFilterIndex;
	if (ofn.nFilterIndex == 0)
		ofn.nFilterIndex = 1; // "All supported types".
	
	//
	// Show the dialog box & load.
	//
	if (::GetOpenFileName (& ofn) == TRUE) {
		// Save ofn.nFilterIndex.
		s_cFilterIndex = ofn.nFilterIndex;

		//
		// Get selected file names.
		//

		string_t strPath;
		file_names_t file_names;

		const char * p = strFilesName;
		while (* p) {
			const char * pEnd = p + ::strlen (p);

			string_t strFileName (p, pEnd);
			if (strPath.empty ())
				strPath.swap (strFileName);
			else
				file_names.push_back (strPath + "\\" + strFileName);

			p = pEnd + 1;
		}

		if (file_names.empty ())
			file_names.push_back (strPath);

		LoadMap (file_names, ofn.nFilterIndex, _bRemoveOldMap, _map);
		if (_bWait)
			WaitLoadThread ();
	}
}

typedef std::vector<const CFileTypeInfo *> vecFileTypeInfos_t;

static
UINT_PTR CALLBACK OFNHookProc (HWND _hWnd, UINT _uiMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uiMsg) {
		case WM_NOTIFY:
			const NMHDR * const pNMH = reinterpret_cast<const NMHDR *> (_lParam);
			switch (pNMH->code) {
				case CDN_TYPECHANGE: {
					//
					// Peplace file name extension according to the newly selected filter.
					//

					const OFNOTIFY * const pOFN = reinterpret_cast<const OFNOTIFY *> (_lParam);
					const vecFileTypeInfos_t * const pvecFTIs = reinterpret_cast<const vecFileTypeInfos_t *> (pOFN->lpOFN->lCustData);
					const size_t cIndex = pOFN->lpOFN->nFilterIndex;

					const CFileTypeInfo * pFTI = NULL;
					if (cIndex - 1 < pvecFTIs->size ())
						pFTI = (* pvecFTIs) [cIndex - 1];

					if (pFTI && ! pFTI->m_vecExts.empty ()) {
						// Update default extension.
						::SendMessage (_hWnd, CDM_SETDEFEXT, 0, reinterpret_cast<LPARAM> (pFTI->m_vecExts [0].c_str ()));

						TCHAR strFileName [MAX_PATH + 1];
						CommDlg_OpenSave_GetFilePath (::GetParent (_hWnd), strFileName, MAX_PATH);

						// Prepare the new extension.
						string_t strExt = ".";
						strExt.append (pFTI->m_vecExts [0]);

						// Replace the extension.
						::PathRenameExtension (strFileName, strExt.c_str ());

						// Show the updated name.
						::SendMessage (::GetParent (_hWnd), CDM_SETCONTROLTEXT, edt1, reinterpret_cast<LPARAM> (::PathFindFileName (strFileName)));
					}
					break;
				}
			}
			return TRUE;
	}
	return FALSE;
}

void CFileTypeInfo::ShowSaveFile () {
	char strFileName [MAX_PATH + 1];
	::strncpy (strFileName, g_strFileName, MAX_PATH);

	const CFileTypeInfo * pSaverInfo = g_pSaverInfo;
	if (! pSaverInfo || (pSaverInfo->m_dwFlags & ftiImportExport)) {
		pSaverInfo = CFileTypeInfo::GetDefaultSaverInfo ();
		assert (pSaverInfo);

		// Force default extension.
		if (pSaverInfo && ! pSaverInfo->m_vecExts.empty ()) {
			string_t strExt = ".";
			strExt.append (pSaverInfo->m_vecExts [0]);
			::PathRenameExtension (strFileName, strExt.c_str ());
		}
	}

	OPENFILENAME ofn;
	::ZeroMemory (& ofn, sizeof (OPENFILENAME));
	ofn.lStructSize = sizeof (OPENFILENAME);
	ofn.hwndOwner = g_hWnd;
	ofn.hInstance = g_hResInst;
	ofn.lpstrFile = strFileName;
	ofn.nMaxFile  = sizeof (strFileName);
	if (pSaverInfo && ! pSaverInfo->m_vecExts.empty ())
		ofn.lpstrDefExt = pSaverInfo->m_vecExts [0].c_str ();
	ofn.lpfnHook  = OFNHookProc;
	ofn.Flags     = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_ENABLESIZING | OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_ENABLEHOOK;

	// Estimate the size of the ofn.lpstrFilter.
	size_t cFilterLen = 1;
	const CFileTypeInfo * p;
	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext)
		if (p->m_pfCreateSaver && (p->m_dwFlags & ftiImportExport) == 0 && p->m_bEnableSave)
			cFilterLen += p->m_strFilter.size () + 1;

	//
	// Prepare ofn.lpstrFilter and ofn.nFilterIndex.
	//

	ofn.nFilterIndex = 1;
	vecFileTypeInfos_t vecFileTypeInfos;

	char * const strFilter = reinterpret_cast<char *> (::alloca (cFilterLen));
	size_t cOffset = 0;

	for (p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if (! p->m_pfCreateSaver || (p->m_dwFlags & ftiImportExport) != 0 || ! p->m_bEnableSave)
			continue;

		// Omit file types which can't be saved.
		if (p != pSaverInfo) {
			std::auto_ptr<CMapSaver> pMapSaver = std::auto_ptr<CMapSaver> (p->m_pfCreateSaver ());
			if (! pMapSaver->CanSave (g_map))
				continue;
		}

		::memcpy (strFilter + cOffset, p->m_strFilter.c_str (), p->m_strFilter.size () + 1);
		cOffset += p->m_strFilter.size () + 1;

		vecFileTypeInfos.push_back (p);
		if (p == pSaverInfo)
			ofn.nFilterIndex = vecFileTypeInfos.size ();
	}
	strFilter [cOffset] = '\0';
	ofn.lpstrFilter = strFilter;

	ofn.lCustData = reinterpret_cast<LPARAM> (& vecFileTypeInfos);

	if (vecFileTypeInfos.empty ()) {
		ErrorMsgBox ("The data can't be saved.");
		return;
	}

	//
	// Show the dialog box & save.
	//
	if (::GetSaveFileName (& ofn) == TRUE) {
		assert (ofn.nFilterIndex - 1 < vecFileTypeInfos.size ());
		pSaverInfo = vecFileTypeInfos [ofn.nFilterIndex - 1];

		SaveMap (pSaverInfo, strFileName);
	}
}

void CFileTypeInfo::InitImportMenu (HMENU _hMenu) {
	size_t cCmd = 0;
	for (const CFileTypeInfo * p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if ((p->m_dwFlags & ftiImportExport) && p->m_pfCreateLoader) {
			::AppendMenu (_hMenu, MF_STRING, ID_FILE_IMPORT_0 + cCmd, p->m_strFilter.c_str ());
			++ cCmd;
		}
	}
}

/////////////////////////////////

struct mi_t {
	UINT uiCmd;
	const char * strFilter;
};

typedef std::map<string_t, mi_t> menus_t;

static
void FillMenusSeaparatedByClass (HMENU _hmenu, const menus_t & _menus) {
	string_t strPrevClass1;
	for (menus_t::const_iterator i = _menus.begin (); i != _menus.end (); ++ i) {
		// Extract first parst of class id.
		const string_t & strClass = i->first;
		const char * const pDash = ::strchr (strClass.c_str (), '-');
		string_t strClass1;
		if (pDash)
			strClass1.assign (strClass.c_str (), pDash);
		else
			strClass1.assign (strClass);

		// For new first part of class id, insert menu delimiter.
		if (! strPrevClass1.empty () && ! (strPrevClass1 == strClass1))
			::AppendMenu (_hmenu, MF_SEPARATOR, -1, NULL);
		strPrevClass1 = strClass1;

		const mi_t & mi = i->second;
		::AppendMenu (_hmenu, MF_STRING, mi.uiCmd, mi.strFilter);
	}
}

HMENU CFileTypeInfo::InitExportMenu (HMENU _hMenu) {
	// Create sub-menu for waypoints/tracks/routes exports.
	const HMENU hmenuWTR = ::CreatePopupMenu ();
	::AppendMenu (_hMenu, MF_STRING | MF_POPUP, reinterpret_cast<UINT_PTR> (hmenuWTR), "Attached Waypoints/Tracks/Routes");
	::AppendMenu (_hMenu, MF_SEPARATOR, -1, NULL);

	// First pass: fill the menu with ordinary exports and prepare data for the waypoints/tracks sub-menu.
	menus_t menus, menusWTR;
	size_t cCmd = 0;
	for (const CFileTypeInfo * p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if ((p->m_dwFlags & ftiImportExport) && p->m_pfCreateSaver && p->m_bEnableSave) {
			const std::pair<menus_t::iterator, bool> r = ((p->m_dwFlags & ftiWTR) ? menusWTR : menus).insert (menus_t::value_type (p->m_strClass, mi_t ()));
			assert (r.second);
				
			mi_t & mi = r.first->second;
			mi.strFilter = p->m_strFilter.c_str ();
			mi.uiCmd     = ID_FILE_EXPORT_0 + cCmd;
			++ cCmd;
		}
	}

	// Second pass: fill the sub-menus.
	FillMenusSeaparatedByClass (_hMenu, menus);
	FillMenusSeaparatedByClass (hmenuWTR, menusWTR);

	DeleteDuplicatedMenuSeparators (_hMenu);

	return hmenuWTR;
}

/////////////////////////////////

void CFileTypeInfo::ShowImportFile (size_t _cIndex) {
	// Search for loader by index.
	size_t cCmd = 0;
	for (const CFileTypeInfo * p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
		if ((p->m_dwFlags & ftiImportExport) && p->m_pfCreateLoader) {
			if (cCmd == _cIndex) {
				//
				// Show file open dialog.
				//

				char * const strFilter = reinterpret_cast<char *> (alloca ((p->m_strFilter.size () + 2)*sizeof (char)));
				::memcpy (strFilter, p->m_strFilter.c_str (), p->m_strFilter.size () + 1);
				strFilter [p->m_strFilter.size () + 1] = '\0';

				char strFilesName [(MAX_PATH + 1)*100 + 1];
				strFilesName [0] = 0;

				OPENFILENAME ofn;
				::ZeroMemory (& ofn, sizeof (OPENFILENAME));
				ofn.lStructSize = sizeof (OPENFILENAME);
				ofn.hwndOwner   = g_hWnd;
				ofn.hInstance   = g_hResInst;
				ofn.lpstrTitle  = "Import";
				ofn.lpstrFile   = strFilesName;
				ofn.nMaxFile    = sizeof (strFilesName);
				ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT;
				ofn.lpstrFilter = strFilter;
				ofn.nFilterIndex = 1;

				if (::GetOpenFileName (& ofn) == TRUE) {
					//
					// Get selected file names.
					//

					string_t strPath;
					file_names_t file_names;

					const char * p = strFilesName;
					while (* p) {
						const char * pEnd = p + ::strlen (p);

						string_t strFileName (p, pEnd);
						if (strPath.empty ())
							strPath.swap (strFileName);
						else
							file_names.push_back (strPath + "\\" + strFileName);

						p = pEnd + 1;
					}

					if (file_names.empty ())
						file_names.push_back (strPath);

					//
					// Perform import.
					//

					LoadMap (file_names, 0, false, g_map);
				}

				return;
			}
			++ cCmd;
		}
	}
}

void CFileTypeInfo::ShowExportFile (size_t _cIndex) {
	try {
		size_t cCmd = 0;
		for (const CFileTypeInfo * p = CFileTypeInfo::s_pFirst; p; p = p->m_pNext) {
			if ((p->m_dwFlags & ftiImportExport) == 0 || ! p->m_pfCreateSaver || ! p->m_bEnableSave)
				continue;

			if (cCmd == _cIndex) {
				const std::auto_ptr<CMapSaver> pSaver = std::auto_ptr<CMapSaver> (p->m_pfCreateSaver ());

				CCritSec::CLock lock (g_cs);

				SetStatus (GetStr (IDS_STATUS_PREPARE_EXPORT));
				SetProgressStatus (0);
				if (! pSaver->PrepareToSave (g_map)) {
					SetStatusReady ();
					return;
				}

				SetStatus (GetStr (IDS_STATUS_EXPORTING));
				SetProgressStatus (0);
				pSaver->Save (NULL, g_strFileName, g_map);

				break;
			}
			++ cCmd;
		}
	} catch (COutOfMemory & _oom) {
		_oom.Report ();
	} catch (const std::exception & _e) {
		ErrorMsgBox ("Unhandled exception: %s.", _e.what ());
	}

	SetStatusReady ();
}